package com.egg.auth.service.role.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.egg.auth.entity.dept.Dept;
import com.egg.auth.entity.menu.Menu;
import com.egg.auth.entity.role.Role;
import com.egg.auth.entity.role.RoleLinkDept;
import com.egg.auth.entity.role.RoleLinkMenu;
import com.egg.auth.entity.role.RoleLinkUser;
import com.egg.auth.enums.menu.MenuTypeEnum;
import com.egg.auth.enums.role.RoleAuthCodeEnum;
import com.egg.auth.enums.role.RoleDataScopeEnum;
import com.egg.auth.mapper.role.RoleMapper;
import com.egg.auth.model.request.web.RolePostOrPut;
import com.egg.auth.model.response.web.RoleInfo;
import com.egg.auth.service.dept.IDeptService;
import com.egg.auth.service.menu.IMenuService;
import com.egg.auth.service.role.IRoleLinkDeptService;
import com.egg.auth.service.role.IRoleLinkMenuService;
import com.egg.auth.service.role.IRoleLinkUserService;
import com.egg.auth.service.role.IRoleService;
import com.egg.common.core.constant.CoreConstant;
import com.egg.common.core.exception.CustomException;
import com.egg.common.core.model.entity.BaseEntity;
import com.egg.common.mybatis.service.impl.BaseServiceImpl;
import com.egg.common.util.JsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;


@Service
public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, Role> implements IRoleService {


    @Autowired
    private IRoleLinkUserService roleLinkUserService;
    @Autowired
    private IRoleLinkMenuService roleLinkMenuService;
    @Autowired
    private IMenuService menuService;
    @Autowired
    private IRoleLinkDeptService roleDataPermService;
    @Autowired
    private IDeptService deptService;



    /**
     * 名称是否重复
     *
     * @param name 名称
     * @return boolean true 存在 / false 不存在
     */
    public boolean existName(String name) {
        return existName(name, Optional.empty());
    }

    /**
     * 名称是否重复
     *
     * @param name     名称
     * @param filterId 排除id
     * @return boolean true 存在 / false 不存在
     */
    public boolean existName(String name, Optional<? extends Serializable> filterId) {
        if (ObjectUtil.isEmpty(name)) {
            return false;
        }
        LambdaQueryWrapper<Role> qw = buildLambdaSortQw();
        qw.eq(Role::getName, name);
        filterId.ifPresent(o -> qw.notIn(Role::getId, o));
        return count(qw) > 0;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void delRole(Collection<? extends Serializable> ids) {
        if (ObjectUtil.isEmpty(ids)) {
            return;
        }
        this.baseDel(ids);
        roleLinkUserService.delByRoleId(ids);
        roleLinkMenuService.delByRoleId(ids);
    }

    @Override
    public List<Role> findByUserId(Serializable userId) {
        List<RoleLinkUser> roleUser = roleLinkUserService.findByUserId(userId);
        if (ObjectUtil.isEmpty(roleUser)) {
            return Collections.emptyList();
        }
        return baseFindById(roleUser.stream().map(o -> o.getRoleId()).collect(Collectors.toList()));
    }

    @Override
    public List<Role> findEnabledByUserId(Serializable userId) {
        List<RoleLinkUser> roleUser = roleLinkUserService.findByUserId(userId);
        if (ObjectUtil.isEmpty(roleUser)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<Role> qw = buildLambdaQw();
        qw.eq(Role::getEnabled, true);
        qw.in(BaseEntity::getId, roleUser.stream().map(o -> o.getRoleId()).collect(Collectors.toList()));
        return list(qw);
    }

    @Override
    public boolean hasAdmin(Role role) {
        if (ObjectUtil.isEmpty(role)) {
            return false;
        }
        return hasAdmin(Arrays.asList(role));
    }

    @Override
    public boolean hasAdmin(List<Role> list) {
        if (ObjectUtil.isEmpty(list)) {
            return false;
        }
        return list.stream()
                .filter(Role::getEnabled)
                .anyMatch(o -> Objects.equals(RoleAuthCodeEnum.ADMIN.getCode(), o.getAuthCode()));
    }

    @Override
    public boolean hasWeb(Role role) {
        if (ObjectUtil.isEmpty(role)) {
            return false;
        }
        return hasWeb(Arrays.asList(role));
    }

    @Override
    public boolean hasWeb(List<Role> list) {
        if (ObjectUtil.isEmpty(list)) {
            return false;
        }
        return list.stream()
                .filter(Role::getEnabled)
                .anyMatch(o -> Objects.equals(RoleAuthCodeEnum.WEB.getCode(), o.getAuthCode()));
    }

    @Override
    public boolean hasApi(Role role) {
        if (ObjectUtil.isEmpty(role)) {
            return false;
        }
        return hasApi(Arrays.asList(role));
    }

    @Override
    public boolean hasApi(List<Role> list) {
        if (ObjectUtil.isEmpty(list)) {
            return false;
        }
        return list.stream()
                .filter(Role::getEnabled)
                .anyMatch(o -> Objects.equals(RoleAuthCodeEnum.API.getCode(), o.getAuthCode()));
    }

    @Override
    public boolean hasRead(List<Role> list) {
        if (ObjectUtil.isEmpty(list)) {
            return false;
        }
        return list.stream()
                .filter(Role::getEnabled)
                .anyMatch(o -> Objects.equals(RoleAuthCodeEnum.READ_ONLY.getCode(), o.getAuthCode()));
    }

    @Override
    public Optional<Role> findByAuthCode(String authCode) {
        if (ObjectUtil.isEmpty(authCode)) {
            return Optional.empty();
        }
        LambdaQueryWrapper<Role> qw = buildLambdaQw();
        qw.eq(Role::getAuthCode, authCode);
        qw.last("LIMIT 1");
        return Optional.ofNullable(getOne(qw));
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void put(Serializable id, RolePostOrPut param) {
        if (RoleAuthCodeEnum.isInvalid(param.getAuthCode())) {
            throw new CustomException(String.format("编码[%s]无效", param.getAuthCode()));
        }
        Role exist = baseGetById(id);
        if (existName(param.getName(), Optional.of(exist.getId()))) {
            throw new CustomException(String.format("名称[%s]已存在", param.getName()));
        }
        Role role = JsonUtil.objToObj(param, Role.class);
        role.setId(exist.getId());
        baseSave(role);
        roleDataPermService.put(role.getId(), param.getDeptId());
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Long add(RolePostOrPut param) {
        if (RoleAuthCodeEnum.isInvalid(param.getAuthCode())) {
            throw new CustomException(String.format("编码[%s]无效", param.getAuthCode()));
        }
        if (existName(param.getName())) {
            throw new CustomException(String.format("名称[%s]已存在", param.getName()));
        }
        Role role = JsonUtil.objToObj(param, Role.class);
        baseSave(role);
        roleDataPermService.put(role.getId(), param.getDeptId());
        return role.getId();
    }

    @Override
    public void fillDept(RoleInfo roleInfo) {
        if (ObjectUtil.isEmpty(roleInfo)) {
            return;
        }
        List<RoleLinkDept> roleLinkDeptList = roleDataPermService.findByRoleId(roleInfo.getId());
        if (ObjectUtil.isEmpty(roleLinkDeptList)) {
            return;
        }
        roleInfo.setDeptId(roleLinkDeptList.stream().map(o -> o.getDeptId()).collect(Collectors.toList()));
    }

    @Override
    public List<String> findFuncPermByUserId(Serializable userId) {
        List<Role> role = findEnabledByUserId(userId);
        if (ObjectUtil.isEmpty(role)) {
            return Collections.emptyList();
        }
        List<RoleLinkMenu> roleLinkMenuList = roleLinkMenuService.findByRoleId(role.stream().map(o -> o.getId()).collect(Collectors.toSet()));
        List<Menu> menuList = menuService.baseFindById(roleLinkMenuList.stream().map(o -> o.getMenuId()).collect(Collectors.toSet()));
        Set<String> permList = menuList.stream()
                .filter(o -> Objects.equals(MenuTypeEnum.BUTTON.getCode(), o.getType()))
                .map(o -> o.getPerm()).collect(Collectors.toSet());
        if (hasAdmin(role)) {
            permList.add(RoleAuthCodeEnum.ADMIN.getPath());
        } else {
            if (hasWeb(role)) {
                permList.add(RoleAuthCodeEnum.WEB.getPath());
            }
            if (hasApi(role)) {
                permList.add(RoleAuthCodeEnum.API.getPath());
            }
            if (hasRead(role)) {
                permList.add(RoleAuthCodeEnum.READ_ONLY.getPath());
            }
        }
        return new ArrayList<>(permList);
    }

    @Override
    public List<Long> findDataPermByUserId(Serializable userId) {
        if (ObjectUtil.isEmpty(userId)) {
            return Collections.emptyList();
        }
        List<Role> enabledRoleList = findEnabledByUserId(userId);
        if (ObjectUtil.isEmpty(enabledRoleList)) {
            // 给与一个不存在的值
            return Arrays.asList(CoreConstant.DEFAULT_ID);
        }
        Set<String> dataScope = enabledRoleList.stream().map(o -> o.getDataScope()).collect(Collectors.toSet());
        if (RoleDataScopeEnum.isAll(dataScope)) {
            // 全部权限，返回空,相当于不做条件判断
            return Collections.emptyList();
        }
        List<Dept> deptList = new ArrayList<>();
        // 这里不用 else if, 因为一位用户可以关联多个部门, 就会存在多重数据权限,这里需要取并集.
        if (RoleDataScopeEnum.isDeptAndChild(dataScope)) {
            // 用户本身部门及其子部门
            deptList.addAll(deptService.findEnabledCurrentAndAllSubByUserId(userId));
        }
        if (RoleDataScopeEnum.isDept(dataScope)) {
            // 用户本身部门
            deptList.addAll(deptService.findEnabledByUserId(userId));
        }
        if (RoleDataScopeEnum.isCustom(dataScope)) {
            // 用户角色自定义部门
            List<RoleLinkDept> roleLinkDeptList = roleDataPermService.findByRoleId(enabledRoleList.stream().map(o -> o.getId()).collect(Collectors.toSet()));
            deptList.addAll(deptService.findEnabledById(roleLinkDeptList.stream().map(o -> o.getDeptId()).collect(Collectors.toSet())));
        }
        if (RoleDataScopeEnum.isOneself(dataScope)) {
            // 用户只能查看自己的数据
            deptList.add(deptService.buildVirtualDept());
        }
        if (ObjectUtil.isEmpty(deptList)) {
            // 如果都没有，那么用户只可以查看自己创建的数据
            // 这里默认构建一个虚假部门,防止查询所有数据
            // 默认没有部门限制,则视为查询所有数据
            deptList.add(deptService.buildVirtualDept());
        }
        return deptList.stream().map(o -> o.getId()).distinct().collect(Collectors.toList());
    }
}
